package com.wontube.smr.bo;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.wontube.client.extension.utils.StringUtils;
import com.wontube.client.extension.utils.net.URLUtils;
import com.wontube.client.json.JsonReader;
import com.wontube.core.cache.CacheConf;
import com.wontube.core.cache.Cacher;
import com.wontube.core.cache.EHCacheKey;
import com.wontube.smr.client.model.po.Country;
import com.wontube.smr.client.model.vo.LocationInfo;
import com.wontube.smr.conf.SmrConfig;
import com.wontube.smr.dao.bo.CountryDao;
import com.wontube.web.geoip.GeoCountry;
import com.wontube.web.geoip.GeoLookupService;


/**
 * @author tanggf(2012-9-17)
 *
 */
public class LocationBO {
	private final static Logger logger = LoggerFactory.getLogger(LocationBO.class);
	private static final String cacheNameCountries = "location_bo.all_countries";
	private static final String cacheNameLocationInfo = "location_bo.location_info";
	
	private static final int timeToLiveSeconds = 60 * 3600;
	private static final int timeToIdleSeconds = 20 * 3600;
	
	private static GeoLookupService service = null;
	private LocationBO() {		
		this.init();
	}
	public final static LocationBO SERVICE = new LocationBO();
	
	public synchronized void init(){
		String dbFile = SmrConfig.SINGLE.getGeoIPDataPath();
		try {
			service = new GeoLookupService(dbFile);
		} catch (IOException e) {
			logger.error("error happen when initial geo ip database.", e);
		}
	}
	
	public static final LocationInfo getLocation(String ip){
		EHCacheKey key = new EHCacheKey(new Object[] { ip });
		LocationInfo location = Cacher.defaultCacher.<LocationInfo>getCache(cacheNameLocationInfo, key);
		if (location != null) {
			return location;
		}
		
		LocationInfo info = new LocationInfo();
		if(service != null){
			GeoCountry country = service.getCountry(ip);
			if( ! country.isUnknown()){				
				info.setCountryCode(country.getCode());
				info.setCountryName(country.getName());
			}
		}else{
			info = getLocationInfoFromHostIp(ip);
		}
		
		checkLocation(info);
		
		if( ! StringUtils.isNullOrEmpty(info.getCountryCode())){
			Cacher.defaultCacher.putCache(cacheNameLocationInfo, key, new CacheConf(timeToLiveSeconds, timeToIdleSeconds), location);
		}
		
		return info;
	}
	
	public static LocationInfo getLocationInfoFromHostIp(String ip){		
		String apiUrl = "http://api.hostip.info/get_json.php?ip=" + ip;

		StringBuilder sb = URLUtils.downloadHtml(apiUrl);
		if (sb.length() == 0) {
			return null;
		}

		LocationInfo location = new LocationInfo();
		String json = sb.toString();
		
		JsonReader reader = new JsonReader(new StringReader(json));
		try {
			reader.beginObject();
			while (reader.hasNext()) {
				String name = reader.nextName();
				if ("country_name".equals(name)) {
					location.setCountryName(reader.nextString());
				} else if ("country_code".equals(name)) {
					location.setCountryCode(reader.nextString());
				} else if ("city".equals(name)) {					
					location.setCity(reader.nextString());
				} else if ("ip".equals(name)) {
					location.setIp(reader.nextString());
				} else {
					reader.skipValue();
				}
			}
			reader.endObject();
		} catch (Exception e) {
			//ignore
		} finally {
			try {
				reader.close();
			} catch (IOException e) {
			}
		}
		
		return location;
	}
	
	private static void checkLocation(LocationInfo info) {	
		List<String> list = getAllCountryCode();
		if (list.contains(info.getCountryCode())) {
			info.setMapedCountryCode(info.getCountryCode());
			info.setMapedCountryName(info.getCountryName());
		} else {
			info.setMapedCountryCode("US");
			info.setMapedCountryName("United States");
		}			
	}	
	
	private static List<String> getAllCountryCode() {
		EHCacheKey key = new EHCacheKey(new Object[0]);
		List<String> list = Cacher.defaultCacher.<ArrayList<String>>getCache(cacheNameCountries, key);
		if (list == null) {
			list = new ArrayList<String>();
			List<Country> countries = CountryDao.listCountries();
			for (Country c : countries) {
				list.add(c.getCountryCode());
			}
			Cacher.defaultCacher.putCache(cacheNameCountries, key, new CacheConf(timeToLiveSeconds, timeToIdleSeconds), list);
		}
		
		return list;
	}
	
	public static void deleteAllCache(){
		Cacher.defaultCacher.deleteAllCache(cacheNameCountries);
	}
}
